package com.gmall.security.filter;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.gmall.common.enums.HttpBizCode;
import com.gmall.common.model.Response;
import com.gmall.common.util.JwtUtil;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 核心鉴权
 */
@Slf4j
public class CoreAuthFilter extends BasicAuthenticationFilter {

    public CoreAuthFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    public CoreAuthFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            // 获取token
            String token = request.getHeader(JwtUtil.TOKEN_HEADER);
            // 没有token 直接放行
            if(StrUtil.isBlank(token) || !token.startsWith(JwtUtil.TOKEN_PREFIX)){
                chain.doFilter(request,response);
                return;
            }

            // 校验token信息
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
            super.doFilterInternal(request, response, chain);
        } catch (Exception e) {
            Response resp = authExceptionHandle(e);
            response.getWriter().write(JSON.toJSONString(resp));
        }
    }

    @Override
    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        //  校验失败
        Response resp = authExceptionHandle(failed);
        response.getWriter().write(JSON.toJSONString(resp));
    }

    private UsernamePasswordAuthenticationToken getAuthentication(String tokenStr){
        // 解析token
        String token = StrUtil.replace(tokenStr,JwtUtil.TOKEN_PREFIX,"");
        // 获取用户名称 过期时间
        String username = JwtUtil.getUsername(token);
        // 获取角色信息
        List<String> roles = JwtUtil.getRoles(token);
        List<GrantedAuthority> authorities = new ArrayList<>();
        Optional.ofNullable(roles).ifPresent(roleList -> roleList.stream().forEach(role ->{
            SimpleGrantedAuthority sg = new SimpleGrantedAuthority(role);
            authorities.add(sg);
        }));
        if(StrUtil.isBlank(username)){
            return new UsernamePasswordAuthenticationToken(username,null,authorities);
        }
        return new UsernamePasswordAuthenticationToken(username,null,authorities);
    }

    public static Response authExceptionHandle(Exception e){
        Response resp = Response.newInstance();
        if(e instanceof AccountExpiredException){
            resp.fail(HttpBizCode.EXPIRED,"抱歉，登录已失效");
        }else if(e instanceof UsernameNotFoundException){
            resp.fail(HttpBizCode.NOT_EXISTS,"用户名不存在");
        }else if(e instanceof BadCredentialsException){
            resp.fail(HttpBizCode.ILLEGAL,"用户名或密码错误");
        }else if(e instanceof ExpiredJwtException){
            resp.fail(HttpBizCode.EXPIRED,"抱歉，登录已失效");
        }else {
            resp.fail(HttpBizCode.ILLEGAL,"获取用户信息失败，请刷新页面重新登录");
            log.error("login error \n {}",e);
        }
        log.error("occur error message : {}", e.getMessage());
        return resp;
    }
}
